[rlc-9/5.14.0-687.15.1.el9_8] crypto: rng - Fix spurious EFAULT in the FIPS per-CPU DRBG#1371
Merged
kerneltoast merged 3 commits intoJun 23, 2026
Conversation
The fast per-CPU DRBG path computes its initial user destination address
straight from the iov_iter. For an ITER_IOVEC iter it reads iter_iov_addr()
and iter_iov_len() of the current segment, but when the iovec leads with
one or more zero-length segments, the current segment is one of those empty
entries. iter_iov_addr() then hands back the base of an empty segment,
which is whatever userspace put there: its base can be NULL or some other
unwritable address, since a zero-length segment is never actually touched.
Right after the setup, that address is prefaulted, and on a bogus base it
fails. A failed prefault on the very first address is treated as fatal, so
the whole read bails out to -EFAULT even though there are perfectly good
non-empty segments later in the iovec. This is reachable with something as
simple as readv() on /dev/urandom where the first iovec entry is {NULL, 0}.
Fix it by advancing the iterator by zero before reading the first address.
The iovec advance loop walks past every leading empty segment and stops at
the first non-empty one, and there's guaranteed to be such a segment
because iov_iter_count() is nonzero at this point. Empty segments that crop
up mid-stream are already skipped by the per-copy advance, so this only
needs to run once during setup.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Sultan Alsawaf <sultan@ciq.com>
While GUP pinning makes it possible to pin the page _backing_ a user address, it *doesn't* pin the page table entry (PTE) for that mapping. This means the pinned physical page can be separated from the user address it was backing, and even back a _different_ user address within the same process. PTE zapping naturally happens during memory reclaim when memory pressure is elevated, and can even be done directly by userspace via madvise(MADV_DONTNEED). Since the optimized per-CPU DRBG loop assumes copy_to_user_nofault() will always succeed on a GUP-pinned page, it immediately bails out when the nofault copy actually *does* fail for the reasons described above. This results in either fewer than requested random bytes copied or, more seriously, a spurious EFAULT returned to userspace when no random bytes were copied. As it turns out, there's no way to pin a PTE. That means it's not possible to guarantee a 100% success rate for the copy_to_user_nofault() attempt. Fix this by handling copy_to_user_nofault() errors correctly with a fall back to a faultable copy attempt outside of the RNG lock. In order to guarantee forward progress for the caller, an on-stack bounce buffer is used to copy up to 256 bytes of the generated random bytes whenever this happens rather than discarding the whole thing. There's no need to use GUP pinning anymore since there's no use for having a page pinned without pinning a PTE to go along with that page, hence the page pinning is eliminated which saves a software page table walk that was performed for _at least_ every destination page. Reported-by: Kun Yi <kunyi@google.com> Signed-off-by: Sultan Alsawaf <sultan@ciq.com>
This reverts commit ef467f3. This helper is no longer used by the FIPS-mode RNG, which was the motivation for reintroducing it. Remove it. Signed-off-by: Sultan Alsawaf <sultan@ciq.com>
|
🤖 Validation Checks In Progress Workflow run: https://github.com/ctrliq/kernel-src-tree/actions/runs/28060404046 |
|
✅ Validation checks completed successfully View full results: https://github.com/ctrliq/kernel-src-tree/actions/runs/28060404046 |
kemotaha
approved these changes
Jun 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
JIRA: GOOGLE-52
These are the FIPS-mode fixes for the fast per-CPU DRBG that backs
/dev/random,/dev/urandom, andgetrandom()underfips=1.The main fix is for a spurious
-EFAULT: the optimized read path copies into the user buffer withcopy_to_user_nofault()under the RNG lock, but a GUP-pinned page doesn't keep its PTE pinned, so the destination can be zapped by reclaim ormadvise(MADV_DONTNEED)and the nofault copy fails. It now falls back to a faultable copy through an on-stack bounce buffer instead of bailing out. The second fix avoids a spurious-EFAULTwhen the iovec leads with a zero-length segment, reachable viareadv()on/dev/urandom. Themm/gupchange reverts thepin_user_pages_fast_only()reintroduction since the EFAULT fix drops GUP pinning entirely.Testing: built under KASAN (+
KASAN_STACK) withfips=1and hammered the RNG read path with:/dev/randomand/dev/urandomgetrandom()/madvise(MADV_DONTNEED)PTE-zap race, 16+16 threads, ~1.88M callsread()/readv()workload with 1 MiB buffers, ~108 GiBZero KASAN reports, and the kernel stayed untainted.
This PR was opened with the assistance of Claude (Opus 4.8).